/*
 * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.lang.ref;

import sun.misc.Cleaner;
import sun.misc.JavaLangRefAccess;
import sun.misc.SharedSecrets;

/**
 * 引用对象的抽象基类。此类定义所有引用对象共有的操作。
 * 由于引用对象与垃圾收集器密切合作实现，此类不能直接被子类化。
 *
 * @author   Mark Reinhold
 * @since    1.2
 */

public abstract class Reference<T> {

    /* Reference 实例处于四种可能的内部状态之一：
     *
     *     Active（活跃）: 受到垃圾收集器的特殊处理。在收集器检测到
     *     引用对象的可达性已更改为适当状态后的某个时间，它会将
     *     实例的状态更改为 Pending 或 Inactive，这取决于
     *     实例在创建时是否注册了队列。在前一种情况下，它还会将
     *     实例添加到待处理引用列表中。新创建的实例是 Active 的。
     *
     *     Pending（待处理）: 待处理引用列表的元素，等待被
     *     Reference-handler 线程入队。未注册的实例永远不会处于此状态。
     *
     *     Enqueued（已入队）: 实例创建时注册的队列的元素。当实例从其
     *     ReferenceQueue 中移除时，它变为 Inactive。未注册的实例
     *     永远不会处于此状态。
     *
     *     Inactive（非活跃）: 无需再做任何事情。一旦实例变为 Inactive，
     *     其状态将永远不会再改变。
     *
     * 状态在 queue 和 next 字段中编码如下：
     *
     *     Active: queue = 实例注册的 ReferenceQueue，或者如果未注册队列
     *     则为 ReferenceQueue.NULL；next = null。
     *
     *     Pending: queue = 实例注册的 ReferenceQueue；next = this
     *
     *     Enqueued: queue = ReferenceQueue.ENQUEUED；next = 队列中的下一个
     *     实例，或者如果在列表末尾则为 this。
     *
     *     Inactive: queue = ReferenceQueue.NULL；next = this。
     *
     * 使用此方案，收集器只需检查 next 字段即可确定 Reference 实例是否
     * 需要特殊处理：如果 next 字段为 null，则实例是活跃的；如果非 null，
     * 则收集器应正常处理该实例。
     *
     * 为了确保并发收集器可以发现活跃的 Reference 对象而不干扰可能对这些
     * 对象应用 enqueue() 方法的应用程序线程，收集器应通过 discovered 字段
     * 链接发现的对象。discovered 字段也用于在待处理列表中链接 Reference 对象。
     */

    private T referent;         /* 被 GC 特殊处理 */

    volatile ReferenceQueue<? super T> queue;

    /* 当 active:   NULL
     *     pending:   this
     *    Enqueued:   队列中的下一个引用（如果是最后一个则为 this）
     *    Inactive:   this
     */
    @SuppressWarnings("rawtypes")
    volatile Reference next;

    /* 当 active:   GC 维护的已发现引用列表中的下一个元素（如果是最后一个则为 this）
     *     pending:   待处理列表中的下一个元素（如果是最后一个则为 null）
     *   otherwise:   NULL
     */
    transient private Reference<T> discovered;  /* 被 VM 使用 */


    /* 用于与垃圾收集器同步的对象。收集器必须在每个收集周期开始时
     * 获取此锁。因此，持有此锁的任何代码都必须尽快完成，不分配新对象，
     * 并避免调用用户代码。
     */
    static private class Lock { }
    private static Lock lock = new Lock();


    /* 等待入队的引用列表。收集器将引用添加到此列表中，
     * 而 Reference-handler 线程将其移除。此列表受上述锁对象保护。
     * 该列表使用 discovered 字段来链接其元素。
     */
    private static Reference<Object> pending = null;

    /* 用于将待处理引用入队的高优先级线程
     */
    private static class ReferenceHandler extends Thread {

        private static void ensureClassInitialized(Class<?> clazz) {
            try {
                Class.forName(clazz.getName(), true, clazz.getClassLoader());
            } catch (ClassNotFoundException e) {
                throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
            }
        }

        static {
            // 预加载并初始化 InterruptedException 和 Cleaner 类
            // 这样如果在运行循环中延迟加载/初始化它们时出现内存不足，
            // 我们就不会遇到麻烦。
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }

        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }

        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }

    /**
     * 如果有待处理的 {@link Reference}，尝试处理它。<p>
     * 返回 {@code true} 作为提示，表示可能还有另一个
     * {@link Reference} 待处理，或者当目前没有更多待处理的
     * {@link Reference} 时返回 {@code false}，程序可以做一些其他
     * 有用的工作而不是循环。
     *
     * @param waitForNotify 如果为 {@code true} 且没有待处理的
     *                      {@link Reference}，等待直到从 VM 收到通知
     *                      或被中断；如果为 {@code false}，当没有待处理的
     *                      {@link Reference} 时立即返回。
     * @return 如果有 {@link Reference} 待处理且已被处理，
     *         或者我们等待通知并且收到了通知或线程在收到通知前被中断，
     *         则返回 {@code true}；否则返回 {@code false}。
     */
    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    // 'instanceof' 有时可能抛出 OutOfMemoryError
                    // 所以在从 'pending' 链中取消链接 'r' 之前执行此操作...
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // 从 'pending' 链中取消链接 'r'
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    // 在锁上等待可能导致 OutOfMemoryError
                    // 因为它可能尝试分配异常对象。
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // 如果等待了则重试
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            // 给其他线程 CPU 时间，希望它们丢弃一些活跃引用
            // 并且 GC 回收一些空间。
            // 同时防止在上面的 'r instanceof Cleaner' 持续抛出 OOME 时
            // 进行 CPU 密集型自旋...
            Thread.yield();
            // 重试
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }

        // Cleaner 的快速路径
        if (c != null) {
            c.clean();
            return true;
        }

        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }

    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        /* 如果有一个大于 MAX_PRIORITY 的特殊系统专用优先级，
         * 它将在这里使用
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();

        // 在 SharedSecrets 中提供访问
        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }

    /* -- 引用对象访问器和设置器 -- */

    /**
     * 返回此引用对象的引用对象。如果此引用对象已被清除，
     * 无论是被程序还是被垃圾收集器清除，则此方法返回 <code>null</code>。
     *
     * @return   此引用所指向的对象，如果此引用对象已被清除则返回
     *           <code>null</code>
     */
    public T get() {
        return this.referent;
    }

    /**
     * 清除此引用对象。调用此方法不会导致此对象被入队。
     *
     * <p> 此方法仅由 Java 代码调用；当垃圾收集器清除引用时，
     * 它直接执行，而不调用此方法。
     */
    public void clear() {
        this.referent = null;
    }

    /* -- 队列操作 -- */

    /**
     * 判断此引用对象是否已被入队，无论是被程序还是被垃圾收集器入队。
     * 如果此引用对象在创建时未注册队列，则此方法将始终返回 <code>false</code>。
     *
     * @return   当且仅当此引用对象已被入队时返回 <code>true</code>
     */
    public boolean isEnqueued() {
        return (this.queue == ReferenceQueue.ENQUEUED);
    }

    /**
     * 清除此引用对象并将其添加到注册的队列中（如果有的话）。
     *
     * <p> 此方法仅由 Java 代码调用；当垃圾收集器将引用入队时，
     * 它直接执行，而不调用此方法。
     *
     * @return   如果此引用对象成功入队则返回 <code>true</code>；
     *           如果它已经入队或在创建时未注册队列则返回 <code>false</code>
     */
    public boolean enqueue() {
        this.referent = null;
        return this.queue.enqueue(this);
    }


    /**
     * 抛出 {@link CloneNotSupportedException}。{@code Reference} 无法
     * 有意义地克隆。请构造一个新的 {@code Reference}。
     *
     * @apiNote 此方法在 Java SE 8 维护版本 4 中定义。
     *
     * @return  永远不会正常返回
     * @throws  CloneNotSupportedException 总是抛出
     *
     * @since 8
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /* -- 构造函数 -- */

    Reference(T referent) {
        this(referent, null);
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }
}
